using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Ltf.Diffs.Diff3Docs.CommonBase;
using Ltf.Diffs.Diff3Docs;

namespace Ltf.Diffs
{
	public partial class MergeOutputControl : UserControl
	{
		public event EventHandler ChangeRegionListBuilt;
		public event UserEditRegionEventHandler UserEditRegion;

		public MergeOutputControl()
		{
			InitializeComponent();
		}



//		private List<ChangeRegion> changeRegions;
		public Output DiffDocument;

		public void ShowOutput(Output difDoc, Diff3Results diff3results)
		{
			DiffDocument = difDoc;
			sciOutput.StartStyling(0, 0x3f);
			sciOutput.StyleSetFont(0, "Courier New");
			sciOutput.StyleSetSize(0, 8);
			sciOutput.SetStyling(0, 0);

			sciOutput.MarginWidthN(0, 40);
			ScintillaDiffDocLoader.ShowMergedOutput(sciOutput, difDoc);
			BuildChangeRegions();
			sciOutput.MouseDwellTime = 400;

			sciOutput.DwellStart += new EventHandler<Scintilla.DwellStartEventArgs>(sciOutput_DwellStart);
			sciOutput.DwellEnd += new EventHandler<Scintilla.DwellEndEventArgs>(sciOutput_DwellEnd);

		}
		void sciOutput_DwellEnd(object sender, Scintilla.DwellEndEventArgs e)
		{
			sciOutput.CallTipCancel(); ;
		}

		void sciOutput_DwellStart(object sender, Scintilla.DwellStartEventArgs e)
		{
			int line = sciOutput.LineFromPosition(e.Position);
			int curMarker = sciOutput.MarkerGet(line);
			List<string> markerComments = new List<string>();
			foreach (long marker in ScintillaDiffDocLoader.MarkerOutputNames.Keys)
			{
				if ((curMarker & (int)Math.Pow(2, marker)) != 0)
					markerComments.Add(ScintillaDiffDocLoader.MarkerOutputNames[marker]);
			}
			if (markerComments.Count > 0)
			{
				string text = string.Join(", ", markerComments.ToArray());
				sciOutput.CallTipShow(e.Position, string.Format(text));
			}
		}

		private void BuildChangeRegions()
		{
			DiffDocument.BuildChangeRegions();
			sciOutput.UsePopup(false);
			if (ChangeRegionListBuilt != null)
				ChangeRegionListBuilt(this, new EventArgs());
		}

		public void FindNext(string text)
		{
			ScintillaExtra seFind = sciOutput;

			int curPos = seFind.CurrentPos;
			seFind.SearchAnchor();
			int pos1 = seFind.SearchNext(0, text);
			if (pos1 == curPos)
			{
				seFind.CurrentPos = curPos + 1;
				seFind.SearchAnchor();
				pos1 = seFind.SearchNext(0, text);
			}
			int bottomLine = seFind.LineFromPosition(seFind.PositionFromPoint(0, seFind.Height));
			int curLine = seFind.LineFromPosition(pos1);
			if (curLine > (bottomLine - 3))
			{
				seFind.GotoLine(seFind.LineFromPosition(pos1));
				pos1 = seFind.SearchNext(0, text);
			}

			//	seFind.GotoLine(seFind.LineFromPosition(pos1));
		}

		public void FindPrevious(string text)
		{
			ScintillaExtra seFind = sciOutput;

			int curPos = seFind.CurrentPos;
			seFind.SearchAnchor();
			int pos1 = seFind.SearchPrevious(0, text);
			if (pos1 == curPos)
			{
				seFind.CurrentPos = curPos - 1;
				seFind.SearchAnchor();
				pos1 = seFind.SearchPrevious(0, text);
			}
			int topLine = seFind.LineFromPosition(seFind.PositionFromPoint(0, 0));
			int curLine = seFind.LineFromPosition(pos1);
			if (curLine < (topLine + 3))
			{
				seFind.GotoLine(seFind.LineFromPosition(pos1));
				pos1 = seFind.SearchPrevious(0, text);
			}

			//	seFind.GotoLine(seFind.LineFromPosition(pos1));
		}

		public override bool Focused
		{
			get
			{
				return sciOutput.Focused;
			}
		}

		protected override void WndProc(ref Message m)
		{
			base.WndProc(ref m);
			if ((int)m.WParam == 0x0204)
				sciOutput_RightClick();
		}

		private int curLineNum;
		private ChangeRegion curChangeRegion;
		private ChangeRegion curChangeRegionAbove;
		private ChangeRegion curChangeRegionBelow;
		private void sciOutput_RightClick()
		{

			Point mp = Control.MousePosition;
			Point mp2 = sciOutput.PointToClient(mp);
			int pos = sciOutput.PositionFromPoint(mp2.X, mp2.Y);

			int lineNum = sciOutput.LineFromPosition(pos);
			curLineNum = lineNum;
			Line curLine = DiffDocument.Lines[lineNum];
			LineState curState = curLine.State;
			LineState curStateAbove = curLine.StateAbove;
			LineState curStateBelow = curLine.StateBelow;
			ChangeRegion region = null;
			foreach (ChangeRegion c in DiffDocument.ChangeRegions)
			{
				if (c.Start <= lineNum && c.End >= lineNum)
				{
					switch (c.Location)
					{
						case ChangeRegionLocation.Here:
							curChangeRegion = c;
							region = c;
							break;
						case ChangeRegionLocation.Below:
							curChangeRegionBelow = c;
							break;
						case ChangeRegionLocation.Above:
							curChangeRegionAbove = c;
							break;
					}
				}
			}
			foreach (ToolStripItem tsi in cmsMerges.Items)
			{
				tsi.Visible = false;
			}

			ContextMenuStrip contextMenu = null;

			List<LineState> undoredostates
				= new List<LineState>(new LineState[] { LineState.MergedOkay, LineState.MergeUndone, LineState.Resolved });

			if (region != null && region.State == ChangeRegionState.Conflict && (region.YourLines.Count > 0 || region.TheirLines.Count > 0))
			{
				useMineAfterTheirsToolStripMenuItem.Visible = (region.TheirLines.Count != 0 && region.YourLines.Count != 0);
				useMineBeforeTheirsToolStripMenuItem.Visible = (region.TheirLines.Count != 0 && region.YourLines.Count != 0);
				useMineToolStripMenuItem.Visible = true; // (conflict.YourLines.Count != 0);
				useTheirsServerToolStripMenuItem.Visible = true; // (conflict.TheirLines.Count != 0);
				useNeiToolStripMenuItem.Visible = true;
				editSectionToolStripMenuItem.Visible = true;
				contextMenu = cmsConflicts;
			}
			else if (undoredostates.Contains(curState)
				|| undoredostates.Contains(curStateAbove)
				|| undoredostates.Contains(curStateBelow))
			{
				tsmUndoMerge.Visible = (curState == LineState.MergedOkay);
				editSectionToolStripMenuItem1.Visible = (curState == LineState.MergedOkay);
				undoMergeAboveToolStripMenuItem.Visible = (curStateAbove == LineState.MergedOkay);
				editMergeSectionAboveToolStripMenuItem.Visible = (curStateAbove == LineState.MergedOkay);
				undoMergeBelowToolStripMenuItem.Visible = (curStateBelow == LineState.MergedOkay);
				editMergeSectionBelowToolStripMenuItem.Visible = (curStateBelow == LineState.MergedOkay);

				tsmRedoMerge.Visible = (curState == LineState.MergeUndone);
				redoMergeAboveToolStripMenuItem.Visible = (curStateAbove == LineState.MergeUndone);
				redoMergeBelowToolStripMenuItem.Visible = (curStateBelow == LineState.MergeUndone);

				tsmUndoResolution.Visible = (curState == LineState.Resolved);
				undoResolutionAboveToolStripMenuItem.Visible = (curStateAbove == LineState.Resolved);
				undoResolutionBelowToolStripMenuItem.Visible = (curStateBelow == LineState.Resolved);

				contextMenu = cmsMerges;

			}
			else
			{
				useMineAfterTheirsToolStripMenuItem.Visible = false;
				useMineBeforeTheirsToolStripMenuItem.Visible = false;
				useMineToolStripMenuItem.Visible = false;
				useTheirsServerToolStripMenuItem.Visible = false;
				useNeiToolStripMenuItem.Visible = false;
				contextMenu = cmsConflicts;
			}
			redoAllMergesToolStripMenuItem.Visible = true;
			undoAllMergesToolStripMenuItem.Visible = true;
			contextMenu.Show(mp);
		}

		private void cmsConflicts_Opening(object sender, CancelEventArgs e)
		{

		}

		private void useMineToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ResolveUseMine();
		}

		private void ResolveUseMine()
		{
			int topline, clinkLine;
			GetTopAndLink(out topline, out clinkLine);
			if (DiffDocument.ResolveUseX(curChangeRegion, false))
			{
				ReShowDocument(topline, clinkLine);
			}
		}

		private void useTheirsServerToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ResolveUseTheirs();
		}

		private void ResolveUseTheirs()
		{
			int topline, clinkLine;
			GetTopAndLink(out topline, out clinkLine);
			if (DiffDocument.ResolveUseX(curChangeRegion, true))
			{
				ReShowDocument(topline, clinkLine);
			}
		}

		private void useMineBeforeTheirsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ResolveUseTheirsBeforeMine();
		}

		private void ResolveUseTheirsBeforeMine()
		{
			int topline, clinkLine;
			GetTopAndLink(out topline, out clinkLine);
			if (DiffDocument.ResolveUseOneAfterOther(curChangeRegion, true))
				ReShowDocument(topline, clinkLine);
		}

		private void ReShowDocument(int topline, int clinkLine)
		{
			ScintillaDiffDocLoader.ShowMergedOutput(sciOutput, DiffDocument);
			sciOutput.LineScroll(0, topline);
			sciOutput.CurrentPos = sciOutput.PositionFromLine(clinkLine);
			sciOutput.SelectionStart = sciOutput.CurrentPos;
			BuildChangeRegions();
		}

		private void useMineAfterTheirsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ResolveUseMineAfterTheirs();
		}

		private void ResolveUseMineAfterTheirs()
		{
			int topline,clinkLine;
			GetTopAndLink(out topline, out clinkLine);
			if (DiffDocument.ResolveUseOneAfterOther(curChangeRegion,false))
				ReShowDocument(topline, clinkLine);
		}

		private void GetTopAndLink(out int topline, out int clinkLine)
		{
			int pos = sciOutput.PositionFromPoint(0, 0);
			topline = sciOutput.LineFromPosition(pos);
			clinkLine = curChangeRegion.Start;
		}

		private void useNeitherToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ResolveUseNeither();
		}

		private void ResolveUseNeither()
		{
			int topline, clinkLine;
			GetTopAndLink(out topline, out clinkLine);
			if (DiffDocument.ResolveUseNeither(curChangeRegion))
				ReShowDocument(topline, clinkLine);
		}

		private void useAllTheirServerFileToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ResolveUseAllTheirs();
		}

		private void ResolveUseAllTheirs()
		{
			int topline, clinkLine;
			GetTopAndLink(out topline, out clinkLine);
			DiffDocument.ResolveUseAllYoursOrTheirs(true);
			ReShowDocument(topline, clinkLine);
		}

		private void useAllMineWorkingCopyFileToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ResolveUseAllMine();
		}

		private void ResolveUseAllMine()
		{
			int topline, clinkLine;
			GetTopAndLink(out topline, out clinkLine);
			DiffDocument.ResolveUseAllYoursOrTheirs(false);

			ReShowDocument(topline, clinkLine);
		}

		private void tsmUndoMerge_Click(object sender, EventArgs e)
		{
			UndoMerge();
		}


		private void tsmRedoMerge_Click(object sender, EventArgs e)
		{
			RedoMerge();
		}

		private void tsmUndoResolution_Click(object sender, EventArgs e)
		{
			UndoResolution();
		}

		private void UndoResolution()
		{
			int topline, clinkLine;
			GetTopAndLink(out topline, out clinkLine);
			if (DiffDocument.UndoResolution(curChangeRegion))
				ReShowDocument(topline, clinkLine);
		}


		private void undoMergeBelowToolStripMenuItem_Click(object sender, EventArgs e)
		{
			curChangeRegion = curChangeRegionBelow;
			UndoMerge();
		}

		private void redoMergeBelowToolStripMenuItem_Click(object sender, EventArgs e)
		{
			curChangeRegion = curChangeRegionBelow;
			RedoMerge();
		}

		private void undoResolutionBelowToolStripMenuItem_Click(object sender, EventArgs e)
		{
			curChangeRegion = curChangeRegionBelow;
			UndoResolution();
		}

		private void undoMergeAboveToolStripMenuItem_Click(object sender, EventArgs e)
		{
			curChangeRegion = curChangeRegionAbove;
			UndoMerge();
		}

		private void redoMergeAboveToolStripMenuItem_Click(object sender, EventArgs e)
		{
			curChangeRegion = curChangeRegionAbove;
			RedoMerge();
		}

		private void undoResolutionAboveToolStripMenuItem_Click(object sender, EventArgs e)
		{
			curChangeRegion = curChangeRegionAbove;
			UndoResolution();
		}


		private void UndoMerge()
		{
			int topline, clinkLine;
			GetTopAndLink(out topline, out clinkLine);
			if (DiffDocument.UndoMerge(curChangeRegion))
				ReShowDocument(topline, clinkLine);
		}

		private void RedoMerge()
		{
			int topline, clinkLine;
			GetTopAndLink(out topline, out clinkLine);
			if (DiffDocument.RedoMerge(curChangeRegion))
				ReShowDocument(topline, clinkLine);
		}


		public void Next()
		{
			int current = sciOutput.LineFromPosition(sciOutput.CurrentPos);
			int next = current;

			int pos = sciOutput.PositionFromPoint(0, 0);
			int oldtopline = sciOutput.LineFromPosition(pos);


			while (next < current + 1 && next >= 0)
				next = sciOutput.MarkerNext(++current, 0xffff);
			sciOutput.GotoLine(next);
			//if (next < sciOutput.LineCount - 10)
			//  sciOutput.LineScroll(0, 10);

			pos = sciOutput.PositionFromPoint(0, 0);
			int newtopline = sciOutput.LineFromPosition(pos);
			if (newtopline > oldtopline + 10)
				sciOutput.LineScroll(0, 10);
			else if (newtopline > oldtopline)
				sciOutput.LineScroll(0, newtopline - oldtopline);

			sciOutput.CurrentPos = sciOutput.PositionFromLine(next);

			sciOutput.Focus();
		}

		public void Previous()
		{
			int current = sciOutput.LineFromPosition(sciOutput.CurrentPos);
			int previous = current;
			int pos = sciOutput.PositionFromPoint(0, 0);
			int oldtopline = sciOutput.LineFromPosition(pos);
			while (previous > current - 1 && previous >= 0)
				previous = sciOutput.MarkerPrevious(--current, 0xffff);
			sciOutput.GotoLine(previous);

			pos = sciOutput.PositionFromPoint(0, 0);
			int newtopline = sciOutput.LineFromPosition(pos);

			if (newtopline < oldtopline - 10)
				sciOutput.LineScroll(0, -10);
			else if (newtopline < oldtopline)
				sciOutput.LineScroll(0, newtopline - oldtopline);
			sciOutput.CurrentPos = sciOutput.PositionFromLine(previous);

			sciOutput.Focus();
		}

		public new string Text
		{
			get { return sciOutput.Text; }
		}

		private void undoAllMergesToolStripMenuItem_Click(object sender, EventArgs e)
		{

		}

		private void redoAllMergesToolStripMenuItem_Click(object sender, EventArgs e)
		{

		}

		private void editSectionToolStripMenuItem_Click(object sender, EventArgs e)
		{
			UserEditRegionEventArgs args = new UserEditRegionEventArgs(curChangeRegion);
			OnUserEdit(args);
		}

		public virtual void OnUserEdit(UserEditRegionEventArgs args)
		{
			if (UserEditRegion != null)
			{
				if (args.RegionToEdit.MergedText != null)
					args.RegionToEdit.PreviousText = new List<string>(args.RegionToEdit.MergedText);
				UserEditRegion(this, args);
				if (!args.Cancel)
				{
					int topline, clinkLine;
					GetTopAndLink(out topline, out clinkLine);
					if (DiffDocument.ResolveUseUserEdit(args.RegionToEdit))
						ReShowDocument(topline, clinkLine);
				}
			}
			else
				MessageBox.Show("No edit function is available.", "Can Not Edit Section",MessageBoxButtons.OK,MessageBoxIcon.Error);
		}

		private void editmergeSectionToolStripMenuItem_Click(object sender, EventArgs e)
		{
			UserEditRegionEventArgs args = new UserEditRegionEventArgs(curChangeRegion);
			OnUserEdit(args);
		}

		private void editMergeSectionBelowToolStripMenuItem_Click(object sender, EventArgs e)
		{
			UserEditRegionEventArgs args = new UserEditRegionEventArgs(curChangeRegionBelow);
			OnUserEdit(args);
		}

		private void editMergeSectionAboveToolStripMenuItem_Click(object sender, EventArgs e)
		{
			UserEditRegionEventArgs args = new UserEditRegionEventArgs(curChangeRegionAbove);
			OnUserEdit(args);
		}

	
	}

	public delegate void UserEditRegionEventHandler (object sender, UserEditRegionEventArgs args);

	public class UserEditRegionEventArgs : EventArgs
	{
		public ChangeRegion RegionToEdit;
		public bool Cancel = false;

		public UserEditRegionEventArgs(ChangeRegion regionToEdit)
		{
			this.RegionToEdit = regionToEdit;
		}

	}
}
